package ast2

import (
	"fmt"
	"strings"
	"sync"

	"gitee.com/u-language/u-language/ucom/astdata"
	"gitee.com/u-language/u-language/ucom/enum"
	"gitee.com/u-language/u-language/ucom/errcode"
	"gitee.com/u-language/u-language/ucom/internal/errutil"
	"gitee.com/u-language/u-language/ucom/internal/utils"
)

// Sbt 符号表 symbolTable
type Sbt struct {
	//单线程符号表
	vmap map[string]fmt.Stringer
	//多线程符号表 key string value Node
	syncmap *sync.Map
	//上级符号表
	sbt *Sbt
	//是否并发
	Thread bool
}

// NewSbt 创建符号表
//   - Thread控制是否可以并发
func NewSbt(Thread bool) *Sbt {
	ret := new(Sbt)
	if Thread {
		ret.syncmap = new(sync.Map)
	} else {
		ret.vmap = make(map[string]fmt.Stringer)
	}
	ret.Thread = Thread
	return ret
}

// seniorSbt 设置上级符号表
//   - sbt是上级符号表
func (v *Sbt) seniorSbt(sbt *Sbt) {
	v.sbt = sbt
}

// add 添加一个符号信息
func (s *Sbt) add(name string, info fmt.Stringer) errcode.ErrCode {
	if s.Thread { //多线程
		_, ok := s.syncmap.LoadOrStore(name, info) //记录到哈希表
		if ok {                                    //如果已经同名符号
			return errcode.SymbolRepeat
		}
	} else { //单线程
		_, ok := s.vmap[name]
		if ok { //如果已经同名符号
			return errcode.SymbolRepeat
		} else {
			s.vmap[name] = info //记录到哈希表
		}
	}
	return errcode.NoErr
}

// store 设置一个符号信息
func (s *Sbt) store(name string, info Node) {
	if s.Thread { //多线程
		s.syncmap.Store(name, info) //记录到哈希表
	} else { //单线程
		s.vmap[name] = info //记录到哈希表
	}
}

// AddVar 记录变量 有错返回错误码
func (s *Sbt) AddVar(n *VarNode) errcode.ErrCode {
	err := s.add(n.Name, n)
	return err
}

// AddConst 记录常量 有错返回错误码
// - n 是被记录的常量节点
func (s *Sbt) AddConst(n *ConstNode) errcode.ErrCode {
	return s.add(n.Name, n)
}

// AddFunc 记录函数
func (s *Sbt) AddFunc(info *FuncNode) errcode.ErrCode {
	return s.add(info.Name, info)
}

// AddStruct 记录结构体 有错返回错误码
//   - decl是语法节点
func (s *Sbt) AddStruct(decl *StructDecl) errcode.ErrCode {
	return s.add(decl.Name, decl)
}

// AddPackage 记录被导入的包 有错返回错误码
//   - p是被导入的包
func (s *Sbt) AddPackage(p *Package) errcode.ErrCode {
	return s.add(p.PackageName, p)
}

// AddMethod 记录方法 有错返回错误码
//   - method是语法节点
func (s *Sbt) AddMethod(method *MethodNode) errcode.ErrCode {
	Type := utils.Ret_no_lea(method.Parame[0].Type.Typ())
	table_name := Type + enum.Method
	info, ok := s.have(table_name)
	if !ok { //如果没有方法表
		table := NewMethodTableInfo(s.Thread)
		s := s
		for s.sbt != nil {
			s = s.sbt
		}
		if s.add(table_name, table) != errcode.NoErr { //并发时万一其他goroutine先添加
			info, _ = s.have(table_name)
		} else {
			info = table
		}
	}
	name := astdata.Generate_method_symbol(Type, method.FuncInfo.Name)
	s.add(name, method)
	v := info.(MethodTableInfo)
	return v.Infos.add(name, method)
}

// AddEnum 记录枚举 有错返回错误码
// - n 是被记录的枚举节点
func (s *Sbt) AddEnum(n *EnumDecl) errcode.ErrCode {
	for s.sbt != nil { //确保记录到全局符号表
		s = s.sbt
	}
	return s.add(n.Name, n)
}

// AddGenInst 记录泛型实例化信息
//   - node是泛型实例化节点
func (s *Sbt) AddGenInst(node *GenericInstantiation) {
	typ := node.Typ()
	if typ == "" {
		return
	}
	name := utils.Ret_no_lea(typ)
	s.add(name, node)
}

// HaveMethod 查询是否记录了方法
//   - Type是类型名
//   - Name是函数的名字
//
// 如果符号不存在，返回空信息
func (s *Sbt) HaveMethod(Type string, Name string) fmt.Stringer {
	defer func() {
		if err := recover(); err != nil {
			if _, ok := err.(errutil.Err2); ok {
				return
			}
			panic(err)
		}
	}()
	Type = utils.Ret_no_lea(Type)
	info, err := s.Have(Type + enum.Method)
	if err != errcode.NoErr {
		return nil
	}
	info, _ = info.(MethodTableInfo).Infos.Have(astdata.Generate_method_symbol(Type, Name))
	return info
}

func (v *Sbt) String() string {
	var buf strings.Builder
	buf.WriteString("*ast2.Sbt{")
	v.Range(func(key string, value fmt.Stringer) bool {
		buf.WriteString("\n")
		buf.WriteString(key)
		buf.WriteString(" : ")
		if _, ok := value.(*Package); !ok {
			buf.WriteString(value.String())
		}
		return true
	})
	buf.WriteString("\t}")
	return buf.String()
}

// Range 循环遍历所有表中记录的符号信息
func (v *Sbt) Range(f func(key string, value fmt.Stringer) bool) {
	if v.Thread {
		v.syncmap.Range(func(key, value interface{}) bool {
			return f(key.(string), value.(fmt.Stringer))
		})
	} else {
		for key, value := range v.vmap {
			if !f(key, value) {
				return
			}
		}
	}
	return
}

func (v *Sbt) Copy() *Sbt {
	ret := NewSbt(v.Thread)
	v.Range(func(key string, value fmt.Stringer) bool {
		ret.add(key, value)
		return true
	})
	ret.sbt = v.sbt
	return ret
}

// Have 查询是否记录了符号
//   - name是符号名
func (v *Sbt) Have(name string) (fmt.Stringer, errcode.ErrCode) {
	value, _, err := v.Have2(name)
	return value, err
}

// Have2 查询是否记录了符号
//   - name是符号名
func (v *Sbt) Have2(name string) (n fmt.Stringer, inlocal bool, err errcode.ErrCode) {
	// if utils.Is_In_AutoFree(name) {
	// 	name = name[:len(name)-len(enum.AutoFreeInFunc)]
	// }
	info, ok := v.have(name)
	if ok {
		return info, true, errcode.NoErr
	}
	info, ok = Builtin_func_info[name]
	if ok {
		return info, true, errcode.NoErr
	}
	if v.sbt != nil { //如果存在上级符号表
		n, err = v.sbt.Have(name)
		return n, false, err
	}
	return nil, false, errcode.UnknownSymbol
}

// 预定义的函数或视为函数调用的类型转换
var Builtin_func_info = map[string]Node{
	enum.Printf:        NewFuncNode(&FuncInfo{Name: enum.Printf, Parame: []astdata.Parame{astdata.NewNameAndType("format", NewObject(TypeObj, enum.String))}}),
	enum.Float:         NewFuncNode(&FuncInfo{Name: enum.Float, RetValue: []astdata.RetValue{astdata.NewNameAndType("", NewObject(TypeObj, enum.Float))}}),
	enum.Int:           NewFuncNode(&FuncInfo{Name: enum.Int, RetValue: []astdata.RetValue{astdata.NewNameAndType("", NewObject(TypeObj, enum.Int))}}),
	enum.Malloc:        NewFuncNode(&FuncInfo{Name: enum.Malloc, Parame: []astdata.Parame{astdata.NewNameAndType("type", NewObject(TypeObj, "T"))}, RetValue: []astdata.RetValue{astdata.NewNameAndType("ptr", NewObject(TypeObj, "&T"))}}),
	enum.Free:          NewFuncNode(&FuncInfo{Name: enum.Free, Parame: []astdata.Parame{astdata.NewNameAndType("ptr", NewObject(TypeObj, "&T"))}}),
	enum.UnsafeAdd:     NewFuncNode(&FuncInfo{Name: enum.UnsafeAdd, Parame: []astdata.Parame{astdata.NewNameAndType("ptr", NewObject(TypeObj, "&T")), astdata.NewNameAndType("offset", NewObject(TypeObj, enum.Int))}, RetValue: []astdata.RetValue{astdata.NewNameAndType("ptr", NewObject(TypeObj, "&T"))}}),
	enum.UnsafeConvert: NewFuncNode(&FuncInfo{Name: enum.UnsafeConvert, Parame: []astdata.Parame{astdata.NewNameAndType("ptr", NewObject(TypeObj, enum.UnsafePointer)), astdata.NewNameAndType("type", NewObject(TypeObj, "T"))}, RetValue: []astdata.RetValue{astdata.NewNameAndType("ptr", NewObject(TypeObj, "&T"))}}),
	enum.UnsafeSizeof:  NewFuncNode(&FuncInfo{Name: enum.UnsafeSizeof, Parame: []astdata.Parame{astdata.NewNameAndType("type", NewObject(TypeObj, "T"))}, RetValue: []astdata.RetValue{astdata.NewNameAndType("size", NewObject(TypeObj, enum.Int))}}),
	enum.MallocSize:    NewFuncNode(&FuncInfo{Name: enum.MallocSize, Parame: []astdata.Parame{astdata.NewNameAndType("size", NewObject(TypeObj, enum.Int))}, RetValue: []astdata.RetValue{astdata.NewNameAndType("ptr", NewObject(TypeObj, enum.UnsafePointer))}}),
	enum.MemPoolFree:   NewFuncNode(nil),
	enum.MemPoolNew:    NewFuncNode(nil),
	enum.UnsafePointer: NewFuncNode(nil),
}

// have 查询是否记录某个符号
func (s *Sbt) have(name string) (fmt.Stringer, bool) {
	if s.Thread { //多线程
		ret, ok := s.syncmap.Load(name)
		if ok {
			return ret.(fmt.Stringer), true
		}
	} else { //单线程
		ret, ok := s.vmap[name]
		return ret, ok
	}
	return nil, false
}

func (s *Sbt) delete(name string) {
	if s.Thread {
		s.syncmap.Delete(name)
		return
	}
	delete(s.vmap, name)
}

var (
	TypeEnumStrMap = map[string]*Object{
		"int":              NewObject(TypeObj, "int"),
		"string":           NewObject(TypeObj, "string"),
		"bool":             NewObject(TypeObj, "bool"),
		"float":            NewObject(TypeObj, "float"),
		enum.UnsafePointer: NewObject(TypeObj, enum.UnsafePointer),
		"any":              NewObject(TypeObj, "any"),
	}
)

// HaveType 查询是否记录了指定类型的符号
//   - name是符号名
func (s *Sbt) HaveType(name string) (astdata.Typ, errcode.ErrCode) {
	name = utils.Ret_no_lea(name)
	if o, ok := TypeEnumStrMap[name]; ok {
		return o, errcode.NoErr
	}
	ret, ok := s.have(name)
	if !ok && s.sbt != nil { //如果存在上级符号表
		return s.sbt.HaveType(name)
	}
	if ret, ok := ret.(astdata.Typ); ok {
		//TODO:更严格的检查是否是类型符号
		return ret, errcode.NoErr
	}
	return nil, errcode.UnknownType
}

// ImportInfo 导入信息
type ImportInfo struct {
	Path string
}

func (info ImportInfo) String() string {
	return fmt.Sprintf("*ast2.ImportInfo{Path:%s}", info.Path)
}

type MethodTableInfo struct {
	Infos *Sbt
}

func NewMethodTableInfo(Thread bool) MethodTableInfo {
	return MethodTableInfo{NewSbt(Thread)}
}

func (info MethodTableInfo) String() string {
	return info.Infos.String()
}
